
#include "rtsql.h"

Sqlite3DB::Sqlite3DB(){
    _pHandle = nullptr;
    _pErrMsg = nullptr;
    _selInfo.value = nullptr;
    // _curTable = "";
}

Sqlite3DB::~Sqlite3DB(){
    close();
}

bool Sqlite3DB::open(const string& file_name){
    int ret = sqlite3_open_v2(file_name.c_str(), &_pHandle, SQLITE_OPEN_READWRITE, nullptr);
    if(ret != SQLITE_OK){
        throw Sqlite3Except("No such SQLite3 file");
    }
    return true;
}

bool Sqlite3DB::close(){
    select_free();
    errmsg_free();
    if(_pHandle != nullptr){
        int status = sqlite3_close(_pHandle);
        while(SQLITE_BUSY == status){
            _pStmt = sqlite3_next_stmt(_pHandle, nullptr);  // 查找有效 stmt
            if(_pStmt != nullptr){
                status = sqlite3_finalize(_pStmt);
                if (status == SQLITE_OK)
                    status = sqlite3_close(_pHandle);
            }else  break;
        }
        _pStmt = nullptr;
        _pHandle = nullptr;

        if(SQLITE_OK == status){
            _print("SQLite close Done")
            return true;
        }
        else{
            throw Sqlite3Except("SQLite Close Error");
        }
    }
    return true;
}

bool Sqlite3DB::assign_table(const string& table_name){  ///
    // 判断是否存在指定的table
    if(false){
        throw Sqlite3Except("No such SQLite table");
    }
    _curTable = table_name;
    return true;
}

void Sqlite3DB::error()
{
    // 由于此处通过 _pErrMsg 是否为空判断，故在 _pErrMsg 产生后必须调用一次 error(),
    // 并将 _pErrMsg 置空。否则将无法在累积多次错误后正常显示错误信息。
    if(_pErrMsg != nullptr){
        printf("SQLite3 错误信息 >> %s\n", _pErrMsg);
        errmsg_free();
    }else{
        // 一般错误消息, 由SQLite内部管理。其内存将会在下产次调用 sqlite3_errmsg() 时被覆盖
        printf("SQLite3 错误信息 >> %s\n", sqlite3_errmsg(_pHandle));
    }
}

void Sqlite3DB::errmsg_free(){
    // 处理通过 sqlite3_exec() 产生的错误信息，由 sqlite3_malloc() 申请，需要手动释放
    if(_pErrMsg != nullptr){
        sqlite3_free(_pErrMsg);
        _pErrMsg = nullptr;
    }
}

void Sqlite3DB::select_free(){
    if(_selInfo.value != nullptr){
        sqlite3_free_table(_selInfo.value);
        _selInfo.row = 0;
        _selInfo.column = 0;
        _selInfo.value = nullptr;
    }
}

int Sqlite3DB::callback_show_table(void *NotUsed, int argc, char **argv, char **azColName)
{
    for(int i = 0; i < argc; i++){
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}

bool Sqlite3DB::exec(const string& strSQL, sqlite3_callback pFunc, void *argv)
{
    errmsg_free();
    if(SQLITE_OK != sqlite3_exec(_pHandle, strSQL.c_str(), pFunc, argv, &_pErrMsg)){
        error();
        throw Sqlite3Except("Fail to exec the SQL command");
    }
    return true;
}

bool Sqlite3DB::prepare(const string& strSQL){
    if(SQLITE_OK != sqlite3_prepare_v2(_pHandle, strSQL.c_str(), -1, &_pStmt, nullptr)){
        throw Sqlite3Except("Prepare Statement Error, check your SQL command");
    };
    return true;
}

bool Sqlite3DB::bind(AutoBind_CallBack callback, void* params, int index){
    // callback 函数用于返回类型和值
    BINDMAP_TYPE type;
    void *value = callback(params, index, &type);

    index += 1;
    if(SQLITE_BIND_int == type){
        sqlite3_bind_int(_pStmt, index, *((int*)value));
    }else if(SQLITE_BIND_double == type){
        sqlite3_bind_double(_pStmt, index, *((double*)value));
    }else if(SQLITE_BIND_string == type){
        char* str = *(char**)value;
        sqlite3_bind_text(_pStmt, index, str, strlen(str), nullptr);
    // }else if(SQLITE_BIND_void == type){
    }else{
        return false;
    }
    return true;
}

bool Sqlite3DB::step(){
    int status = sqlite3_step(_pStmt);
    if(SQLITE_DONE == status){
        _print("SQLite Step Done");
        sqlite3_reset(_pStmt);
        sqlite3_clear_bindings(_pStmt);
//  }else if(SQLITE_OK == status){
//      _print("SQLite Step OK");
    }else if(SQLITE_ROW == status){
        _print("SQLite Step need to show the infomation.");
    }else if(SQLITE_BUSY == status){
        _print("SQLite Step Busy, try again.")
    }else if(SQLITE_ERROR == status){
        error();
        throw Sqlite3Except("SQLite Step Error");
    }else if(SQLITE_MISUSE == status){
        throw Sqlite3Except("SQLite Step Misuse");
    }else{
        throw Sqlite3Except("SQLite Step Unkown RetValue, Check it");
    }
    return true;
}

bool Sqlite3DB::show(const string& where, string table_name)
{
    if(table_name.empty())  table_name = _curTable;
    char *sql;
    if(where.empty())
        asprintf(&sql, "select * from %s;", table_name.c_str());
    else
        asprintf(&sql, "select * from %s where %s;", table_name.c_str(), where.c_str());
    exec(sql, callback_show_table, nullptr);
    free(sql);
    return true;
}

int Sqlite3DB::select(const string& where, string table_name)
{
    if(table_name.empty())  table_name = _curTable;
    char *sql;
    if(where.empty())
        asprintf(&sql, "select * from %s;", table_name.c_str());
    else
        asprintf(&sql, "select * from %s where %s;", table_name.c_str(), where.c_str());

    select_free();  // 清理查询数据
    errmsg_free();  // 清理错误消息
    if(SQLITE_OK != sqlite3_get_table(_pHandle, sql, &_selInfo.value, &_selInfo.row, &_selInfo.column, &_pErrMsg)){
        error();
        throw Sqlite3Except("SQLite Select Error");
    }
    return _selInfo.row;
}

char* Sqlite3DB::get_sel_value(int row, int column){
    if(_selInfo.value == nullptr){
        throw Sqlite3Except_Select("SQLite Select None");
    }
    if(row > _selInfo.row || column > _selInfo.column){
        throw Sqlite3Except_Select("SQLite Select out of range");
    }
#if 0
    printf("Select the string : %s\n", _selInfo.value[(row+1)*_selInfo.column+column]);
    for(int i=0; i<=_selInfo.row; i++){
        for(int j=0; j<_selInfo.column; j++){
            printf("row %d  column %d : %s\n", i, j, _selInfo.value[i*_selInfo.column+j]);
        }
    }
#endif
    return _selInfo.value[(row+1)*_selInfo.column+column];
}

char* Sqlite3DB::select(const string& where, int row, int column, string table_name){
    if(0 < select(where, table_name))
        return get_sel_value(row, column);
    else
        throw Sqlite3Except_Select("SQLite Select None");
}

bool Sqlite3DB::lock(const string& passwd){
    int status = sqlite3_rekey(_pHandle, passwd.c_str(), passwd.size());
    if(SQLITE_NOTADB == status){
        // raise(Encrypt_Err, "Database is already encrypted")
        return false;
    }else if(SQLITE_OK != status){
        error();
        throw Sqlite3Except_Key("SQLite failed to lock the key");
    }
    return true;
}

bool Sqlite3DB::unlock(const string& passwd){
    if(SQLITE_OK != sqlite3_key(_pHandle, passwd.c_str(), passwd.size())){
        throw Sqlite3Except_Key("SQLite failed to unlock the key");
    }
    return true;
}

bool Sqlite3DB::setkey(const string& passwd, const string& new_passwd){
    if(new_passwd.empty()){  // 如 new_passwd 为空，则清除密码
        return clearkey(passwd);
    }
    // 若passwd为空，直接加密
    if(!lock(new_passwd)){
        // 解密当前数据库
        unlock(passwd);
    }
    return lock(new_passwd);
}

bool Sqlite3DB::clearkey(const string& passwd){
    // 解密当前数据库
    unlock(passwd);
    return SQLITE_OK == sqlite3_rekey(_pHandle, nullptr, 0);
}
